using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using log4net;
using Reinvention.Handler.Handlers;

namespace Reinvention.Handler
{
    public class HandlerService : IHandlerService
    {
        readonly ICachingService _cachingService;
        readonly ILog _logger = LogManager.GetLogger(typeof(HandlerService));

        public HandlerService(ICachingService cachingService)
        {
            _cachingService = cachingService;
        }

        public IResult Handle(IItem item)
        {
            var handler = FindHandler<IItem>();
            if (handler != null)
            {
                _logger.DebugFormat("Handling {0} with {1}.", item.GetType().Name, handler.GetType().Name);
                return handler.Handle(item);
            }

            var errorMessage = string.Format("Handler for {0} was not found.", item.GetType().Name);
            _logger.Info(errorMessage);
                
            throw new Exception(errorMessage);
        }

        public IItemHandler FindHandler<T>() where T : IItem
        {
            var handlers = GetHandlers();
            var handler = handlers
                .Where(h => typeof(T).Name + "Handler" == h.GetType().Name)
                .FirstOrDefault();

            return handler ?? new HandlerNotFound();
        }

        IEnumerable<IItemHandler> GetHandlers()
        {
            _logger.Debug("Loading...");

            var handlers = _cachingService.GetCache<IItemHandler>();
            if (handlers == null || handlers.Count() == 0)
            {
                _logger.Debug("Cache is empty. Loading from Assembly...");

                var handlerTypes = Assembly.GetExecutingAssembly().GetTypes()
                    .Where(x => typeof(IItemHandler).IsAssignableFrom(x) && x.IsClass);
                handlers = handlerTypes
                    .Select(x => Activator.CreateInstance(x) as IItemHandler)
                    .ToList();
                _cachingService.SetCache(handlers);

                _logger.Debug("Saving to cache...");
            }

            return handlers;
        }
    }
}